package com.lemon.cloud.command.annotation;

import com.lemon.cloud.command.CommandExecutor;
import com.lemon.cloud.command.CommandRepository;
import com.lemon.cloud.command.CommandInvocation;
import com.lemon.cloud.representation.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.ParameterizedType;

/**
 * @author lemon
 * @return
 * @description
 * @date 2019-09-15 09:29
 */
@Slf4j
@Component
public class CommandAnnotationBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Autowired
    private CommandRepository commandRepository;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().isAnnotationPresent(Command.class)
                && CommandExecutor.class.isAssignableFrom(bean.getClass())) {
            this.registerCommand(bean);
        }

        return bean;
    }

    /**
     * @param bean
     * @return void
     * @description
     * @author lemon
     * @date 2019-09-15 17:47
     */
    private void registerCommand(Object bean) {
        Class commandExecutorClass = bean.getClass();
        Class<? extends Response> responseClass = (Class<Response>) (((ParameterizedType) (commandExecutorClass.getGenericInterfaces()[0])).getActualTypeArguments())[0];
        Class<? extends com.lemon.cloud.command.Command> commandClass = (Class<com.lemon.cloud.command.Command>) (((ParameterizedType) (commandExecutorClass.getGenericInterfaces()[0])).getActualTypeArguments())[1];

        CommandInvocation commandInvocation = this.applicationContext.getBean(CommandInvocation.class);
        commandInvocation.setCommandExecutor((CommandExecutor) this.applicationContext.getBean(commandExecutorClass));
        commandInvocation.setResponseInstance(this.getResponseInstance(responseClass));
        commandInvocation.setPreCommandInterceptors(this.commandRepository.getGlobalPreCommandInterceptorList());
        commandInvocation.setPostCommandInterceptors(this.commandRepository.getGlobalPostCommandInterceptorList());

        this.commandRepository.registered(commandClass, commandInvocation);
    }

    /**
     * @param responseClass
     * @return com.lemon.cloud.representation.Response
     * @description
     * @author lemon
     * @date 2019-09-15 08:49
     */
    private <R extends Response> R getResponseInstance(Class<? extends Response> responseClass) {
        try {
            return (R) responseClass.newInstance();
        } catch (Exception e) {
            log.error(String.format("Response[%S]实例化失败", responseClass), e);
            return (R) new Response();
        }
    }
}